﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Mario.Animation;
using Mario.Structures;
namespace Mario.Characters
{
    enum FaceDirection
    {
        Left = -1,
        Right = 1,
    }
    class Enemy
    {
        public Level Level
        {
            get { return level; }
        }
        Level level;

        public Vector2 Position
        {
            get { return position; }
        }
        Vector2 position;

        private Rectangle localBounds;
    
        public Rectangle BoundingRectangle
        {
            get
            {
                int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X;
                int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y;

                return new Rectangle(left, top, localBounds.Width, localBounds.Height);
            }
        }
        public bool IsAlive { get; private set; }

        // Animations
        private Animation.Animation runAnimation;
        private Animation.Animation idleAnimation;
        private Animation.Animation dieAnimation;
        private SoundEffect killedSound;
        private AnimationPlayer sprite;
        private FaceDirection direction = FaceDirection.Left;

        private float previousBottom;
        private const float GravityAcceleration = 3500.0f;
        private const float MaxFallSpeed = 600.0f;
        public bool IsOnGround
        {
            get { return isOnGround; }
        }
        bool isOnGround;
        internal FaceDirection Direction
        {
            get { return direction; }
        }

        private float waitTime;

        private const float MaxWaitTime = 0.5f;
        private const float MoveSpeed = 30.0f;

        public Enemy(Level level, Vector2 position, string spriteSet)
        {
            this.level = level;
            this.position = position;

            LoadContent(spriteSet);
            this.IsAlive = true;

        }

        public void LoadContent(string spriteSet)
        {
            // Load animations.
            spriteSet = "Sprites/Enemies/" + spriteSet + "/";
            runAnimation = new Animation.Animation(Level.Content.Load<Texture2D>(spriteSet + "Walking"), 0.1f, true);
            idleAnimation = new Animation.Animation(Level.Content.Load<Texture2D>(spriteSet + "Idle"), 0.15f, true);
            dieAnimation = new Animation.Animation(Level.Content.Load<Texture2D>(spriteSet + "Die"), 0.07f, false);

            sprite.PlayAnimation(idleAnimation);
            killedSound = Level.Content.Load<SoundEffect>("Sounds/bump");

            // Calculate bounds within texture size.
            int width = (int)(idleAnimation.FrameWidth * 0.35);
            int left = (idleAnimation.FrameWidth - width) / 2;
            int height = (int)(idleAnimation.FrameWidth * 0.7);
            int top = idleAnimation.FrameHeight - height;
            localBounds = new Rectangle(left, top, width, height);
        }

        public void Update(GameTime gameTime)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            // Calculate tile position based on the side we are walking towards.
            float posX = Position.X + localBounds.Width / 2 * (int)direction;
            int tileX = (int)Math.Floor(posX / Tile.Width) - (int)direction;
            int tileY = (int)Math.Floor(Position.Y / Tile.Height);
            // Level.GetCollision(tileX + (int)direction * 2, tileY - 1) == TileCollision.Invisible ||
           
            //if (Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Passable && Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Invisible)
            //{

            if (Level.GetCollision(tileX + (int)direction, tileY) != TileCollision.Passable || (Level.GetCollision(tileX + (int)direction, tileY) == TileCollision.Passable && Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Invisible))
            {
                if (waitTime > 0)
                {
                    // Wait for some amount of time.
                    waitTime = Math.Max(0.0f, waitTime - (float)gameTime.ElapsedGameTime.TotalSeconds);
                    if (waitTime <= 0.0f)
                    {
                        // Then turn around.
                        direction = (FaceDirection)(-(int)direction);
                    }
                }
                else
                {
                    // If we are about to run into a wall or off a cliff, start waiting.
                    if (
                        Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Invisible ||
                        Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Breakable ||
                        Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Impassable ||
                        Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Platform ||
                        Level.GetCollision(tileX + (int)direction, tileY - 1) == TileCollision.Special
                        )
                    {
                        waitTime = MaxWaitTime;
                    }
                    else
                    {
                        // Move in the current direction.
                        Vector2 velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
                        position = position + velocity;
                        // HandleCollisions();
                    }
                }
            }
            else
            {
                // Move in the current direction.
                Vector2 velocity = new Vector2((int)direction * MoveSpeed * elapsed, 0.0f);
                position.Y += 3;
                position = position + velocity;
                // HandleCollisions();
            }
        }
        private void HandleCollisions()
        {
            // Get the player's bounding rectangle and find neighboring tiles.
            Rectangle bounds = BoundingRectangle;
            int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
            int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
            int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
            int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

            // Reset flag to search for ground collision.
            isOnGround = false;

            // For each potentially colliding tile,
            for (int y = topTile; y <= bottomTile; ++y)
            {
                for (int x = leftTile; x <= rightTile; ++x)
                {
                    // If this tile is collidable,
                    TileCollision collision = Level.GetCollision(x, y);
                    if (collision != TileCollision.Passable)
                    {
                        // Determine collision depth (with direction) and magnitude.
                        Rectangle tileBounds = Level.GetBounds(x, y);
                        Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
                        if (depth != Vector2.Zero)
                        {
                            float absDepthX = Math.Abs(depth.X);
                            float absDepthY = Math.Abs(depth.Y);

                            // Resolve the collision along the shallow axis.
                            if (absDepthY < absDepthX || collision == TileCollision.Platform)
                            {
                                // If we crossed the top of a tile, we are on the ground.
                                if (previousBottom <= tileBounds.Top)
                                    isOnGround = true;

                                // Ignore platforms, unless we are on the ground.
                                if (collision == TileCollision.Impassable || IsOnGround)
                                {
                                    // Resolve the collision along the Y axis.
                                    position = new Vector2(position.X, position.Y + depth.Y);

                                    // Perform further collisions with the new bounds.
                                    bounds = BoundingRectangle;
                                }
                            }
                            else if (collision == TileCollision.Impassable) // Ignore platforms.
                            {
                                // Resolve the collision along the X axis.
                                position = new Vector2(position.X + depth.X, position.Y);

                                // Perform further collisions with the new bounds.
                                bounds = BoundingRectangle;
                            }
                        }
                    }
                }
            }

            // Save the new bounds bottom.
            previousBottom = bounds.Bottom;
        }
        public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
        {
            // Stop running when the game is paused or before turning around.
            if (!IsAlive)
            {
                sprite.PlayAnimation(dieAnimation);
            }
            else if (!Level.Player.IsAlive ||
               Level.ReachedExit ||
               Level.TimeRemaining == TimeSpan.Zero ||
               waitTime > 0)
            {
                sprite.PlayAnimation(idleAnimation);
            }
            else
            {
                sprite.PlayAnimation(runAnimation);
            }


            if (IsAlive)
            {
                // Draw facing the way the enemy is moving.
                SpriteEffects flip = direction > 0 ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
                sprite.Draw(gameTime, spriteBatch, Position, flip);
            }
        }
        public void OnKilled(Player killedBy)
        {
            IsAlive = false;
            killedSound.Play();
        }

    }
}
